﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Compilex.CodeGeneration
{
    internal class Function
    {
        private CodeGenerator Generator { get; set; }
        private StringBuilder customBuilder = null;
        private StringBuilder CodeBuilder
        {
            get
            {
                return customBuilder == null ? Generator.CodeBuilder : customBuilder;
            }
        }

        internal Function(CodeGenerator generator, StringBuilder blder)
        {
            this.Generator = generator;
            this.customBuilder = blder;
        }

        internal void Prologue(string functionName, int localVariablesSizeBytes)
        {
            CommonOperations ops = new CommonOperations(Generator, CodeBuilder);

            ops.Comment("**** FUNÇÃO " + functionName + "****");
            ops.Nop(Generator.Labels.GetFunctionLabel(functionName));

            CallStack callstack = new CallStack(Generator, CodeBuilder);
            callstack.PushWord("BP");
            ops.Comment("novo bp : bp <- sp  ");
            ops.Move("BP", "SP");

            // reserva espaço para variáveis locais
            ops.Comment("reserva de variaveis locais  ");
            ops.Sub("SP", localVariablesSizeBytes);
            ops.StoreTo("SP");
        }

        internal void Epilogue(int stackSize, int localVariablesSize, string functionName)
        {
            CommonOperations ops = new CommonOperations(Generator, CodeBuilder);
            ops.Comment(" epílogo");
            ops.Nop(Generator.Labels.GetEpilogueLabel(functionName));
            ops.Add("SP", stackSize);
            ops.StoreTo("SP");

            ops.Comment("BP = *BP");
            ops.LoadFromPointer("BP", 0);
            ops.StoreTo("BP");
            //ops.MoveToPointer("BP", "BP");

            // jump para end retorno
            ops.LoadFromPointer("SP", -(stackSize - 2 - localVariablesSize));
            ops.JumpToAddressInAcc();

        }

        internal void PushReturn()
        {
            new CommonOperations(Generator, CodeBuilder)
                .MoveToPointer("BP", 4, Generator.Labels.GetResultLabel());
        }

        internal void StoreToLocalVariable(string labelContent, int variableOffset)
        {
            var ops = new CommonOperations(Generator, CodeBuilder);
            ops.MoveToPointer("BP", -variableOffset, labelContent);
        }

        internal void LoadLocalVariable(int variableOffset)
        {
            var ops = new CommonOperations(Generator, CodeBuilder);
            ops.LoadFromPointer("BP", -variableOffset);
        }

        internal void LoadArgument(int parameterOffset)
        {
            var ops = new CommonOperations(Generator, CodeBuilder);
            ops.LoadFromPointer("BP", parameterOffset);
        }

        internal void StoreToArgument(string labelContent, int parameterOffset)
        {
            var ops = new CommonOperations(Generator, CodeBuilder);
            ops.MoveToPointer("BP", parameterOffset, labelContent);
        }
    }
}
